package com.zte.claa.inficombo.csa.app.service.rptparse.ext.claa;

import java.sql.Timestamp;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.zte.claa.inficombo.csa.app.dao.mysql.impl.DevCmdConfDataMapper;
import com.zte.claa.inficombo.csa.app.model.dev.ClaaDevInitState;
import com.zte.claa.inficombo.csa.app.model.dev.CmdModelBean;
import com.zte.claa.inficombo.csa.app.model.dev.DevCmdConfData;
import com.zte.claa.inficombo.csa.app.service.csa.CSAMainService;
import com.zte.claa.inficombo.csa.app.service.dev.CSADevtypeService;
import com.zte.claa.inficombo.csa.app.service.persistent.CSADataPersistService;
import com.zte.claa.inficombo.csa.app.util.JsonUtils;

/**
 * 中兴克拉自研设备初始化配置数据请求处理服务实现类。
 * 
 * 主要逻辑：
 * 1、缓存req；
 * 2、根据req查询对应的配置命令cmdconfdata；
 * 3、根据cmdconfdata执行命令（需要响应模式，mina框架会将响应记录日志）；
 * 4、执行完req后修改配置命令的执行标记；
 * 
 * 5、顺便执行一下设备初始化状态（初始化是否起效、版本、备注等）数据的持久化或更新.
 * 
 * @author 0283000115
 */

@Service
public class ClaaDevConfDataSenderService implements InitializingBean, DisposableBean, IClaaDevConfDataSenderService {	
	public static final int CODE_OK = 200;
	public static final int CODE_OK_TOIWG = 201;
	public static final int CODE_OK_TOBUF = 202;
	public static final int CODE_OK_REJOIN = 203;
	
	// 日志记录
	private final Logger logger = LoggerFactory.getLogger(ClaaDevConfDataSenderService.class);
	
	@Autowired
	private CSAMainService csaMainService;
	
	@Autowired
	private CSADevtypeService devtpService;
	
	@Autowired
	private DevCmdConfDataMapper devCmdConfMapper;
	
	@Autowired
	private CSADataPersistService persistService;
	
	// 请求分发线程数.
	@Value("${csa.claa.devconf.sender.num}")
	private int devConfSenderNum = 2;

	// 请求队列大小.
	@Value("${csa.claa.devconf.queue.size}")
	private int reqQueueSize = 5000;
	
	// 内部分发队列...
	private Thread[] dataDealears = null;
	private LinkedBlockingQueue<ConfDataRequest> queue = null;
	private boolean isWorkOn = true;
	
	// 设备初始化状态数据队列.
	private LinkedBlockingQueue<ClaaDevInitState> devStQueue = null;
	
	// req分发线程数读写方法.
	public int getDevConfSenderNum() {
		return devConfSenderNum;
	}

	public void setDevConfSenderNum(int devConfSenderNum) {
		this.devConfSenderNum = devConfSenderNum;
	}

	public int getReqQueueSize() {
		return reqQueueSize;
	}

	public void setReqQueueSize(int reqQueueSize) {
		this.reqQueueSize = reqQueueSize;
	}

	/**
	 * 服务初始化.
	 */
	@Override
	public void afterPropertiesSet() throws Exception {
		// 初始化缓冲队列.
		this.devStQueue = new LinkedBlockingQueue<ClaaDevInitState>(reqQueueSize);
		this.queue = new LinkedBlockingQueue<ConfDataRequest>(reqQueueSize);
		this.isWorkOn = true;
		this.dataDealears = new Thread[devConfSenderNum];
		// 启动分发线程。。。
		for (int i = 0; i < devConfSenderNum; i++) {
			this.dataDealears[i] = new Thread(new Runnable() {
				@Override
				public void run() {
					dealClaaDevConfReq();
				}
			});
			this.dataDealears[i].setName("ClaaDevConfReq-dispatcher-" + i);
			this.dataDealears[i].start();
		}	
		Thread devStWorker = new Thread(new Runnable() {
			@Override
			public void run() {
				dealClaaDevInistData();
			}
		});		
		devStWorker.setName("ClaaDevInitState-save-thread");
		devStWorker.start();
		logger.info("ClaaDevConfDataSenderService is inited ok (dispatcher num="+devConfSenderNum+").");
	}
	
	/**
	 * 退出销毁资源.
	 */
	@Override
	public void destroy() throws Exception {
		this.isWorkOn = false;
		if(this.queue != null){
			this.queue.clear();
			for(int i=0; i<devConfSenderNum; i++){
				this.queue.offer(new ConfDataRequest());
			}
		}
		
		if(this.devStQueue != null){
			this.devStQueue.clear();
			this.devStQueue.offer(new ClaaDevInitState());
		}
		logger.warn("ClaaDevConfDataSenderService is destroyed!");
	}
	
	/**
	 * 接收claa-dev的初始化配置请求...
	 */
	@Override
	public boolean submitConfDataRequest(ConfDataRequest req) {
		// 1.添加到linkedqueue中，等待解析处理...
		boolean b = this.queue.offer(req);
		if(b == false){
			logger.warn("Add req to queue failed: "+req);
		}
		// 2.检查是否有初始化状态数据上报.
		if(req.hasDevInitStateData()){
			ClaaDevInitState state = new ClaaDevInitState();
			state.setDeveui(req.getDeveui());
			state.setDevtype(req.getDevtype());
			state.setProjectid(req.getProjectid());
			state.setRemark(req.getRemark());
			state.setVersion(req.getVersion());
			this.submitClaaDevInitState(state);
		}
		return b;
	}
	
	/**
	 * 设备状态ClaaDevInitState提交（主要用于GSP的版本上报消息处理）
	 */
	public void submitClaaDevInitState(ClaaDevInitState devist) {
		this.devStQueue.offer(devist);
	}
	
	/**
	 * 分配处理数据.
	 */
	private void dealClaaDevInistData() {
		while (this.isWorkOn) {
			try {
				ClaaDevInitState stData = this.devStQueue.poll(60, TimeUnit.SECONDS);
				if(stData == null){
					logger.debug("No claa dev init state data available now!");
					continue;
				} 
				String devEui = stData.getDeveui();
				if(devEui == null || devEui.trim().equals("")){
					logger.warn("[*@@*]Invalid deveui: "+devEui);
					continue;
				}
				this.persistService.saveClaaDevInitState(stData);
				logger.info("Process ClaaDevInitState ok: "+stData);
			} catch (Throwable t) {
				logger.error("dealClaaDevInistData failed: " + t.getMessage(), t);
			}
		}
		logger.warn("Dealer thread exit: " + Thread.currentThread().getName());
	}
	
	/**
	 * 分配处理数据.
	 */
	private void dealClaaDevConfReq() {
		while (this.isWorkOn) {
			try {
				ConfDataRequest reqData = this.queue.poll(60, TimeUnit.SECONDS);
				if(reqData == null){
					logger.debug("No claa dev conf req data available now!");
					continue;
				} 
				logger.info("Begin process req: "+reqData);
				String devEui = reqData.getDeveui();
				String cmdCode = reqData.getCmdcode();
				DevCmdConfData dccfData = this.devCmdConfMapper.queryDevCmdConfData(devEui, cmdCode);
				if(dccfData == null){
					logger.warn("[*@*]No conf data or inactive for claa dev: "+devEui);
					continue;
				}
				this.execOneCmd(dccfData, reqData);	
			} catch (Throwable t) {
				logger.error("dealClaaDevConfReq failed: " + t.getMessage(), t);
			}
		}
		logger.warn("Dealer thread exit: " + Thread.currentThread().getName());
	}

	/**
	 * 执行命令。
	 * 
	 * @param cmdData
	 * @param reqData
	 * @return 0--成功；1--失败；
	 */
	private int execOneCmd(DevCmdConfData cmdData, ConfDataRequest reqData){	
		logger.info("Begin to exec cmd: "+cmdData);	
		int ret = 0;
		try {
			// 查询命令类型.
			List<CmdModelBean> cmModelList = this.devtpService.getDevtypeCmdModel(cmdData.getDevtpid());
			CmdModelBean cm = cmModelList.get(0);
			Byte cfb = cm.getConfirm();
			boolean needConfirm = (cfb.byteValue() == CmdModelBean.CONFIRM_YES);
			byte cmdtype = cm.getCmdtype();
			Map<String, Object> extParaMap = null;
			if(reqData.getPkgmode() == ConfDataRequest.PKG_MODE_SPECIAL){
				extParaMap = new HashMap<String, Object>();
				extParaMap.put(ConfDataRequest.PKG_INDEX_PARA_NAME, reqData.getPkgindex());
			}
			String retJsonStr = "";
			if(cmdtype == CmdModelBean.CMD_TP_NORMAL){
				retJsonStr = this.csaMainService.SendDevCtrlCmd(reqData.getAppeui(), cmdData.getDeveui(), needConfirm,
						cmdData.getDevport(), cmdData.getDatajson(), extParaMap);
			} else if(cmdtype == CmdModelBean.CMD_TP_BRCAST){
				retJsonStr = this.csaMainService.SendBcastCmd(reqData.getAppeui(), cmdData.getDevport(), 3,
						cmdData.getDevtpid(), cmdData.getDatajson(), extParaMap);
			} else {
				logger.warn("Unknown cmd type: "+cmdtype);
				return 0;
			}
			int retcode = this.parseAckJson(retJsonStr);
			if(this.isExecSucc(retcode)){
				// 修改下发标记和下发执行时间...
				cmdData.setExecflag(DevCmdConfData.EXEC_FLAG_YES);
				cmdData.setExectime(new Timestamp(System.currentTimeMillis()));
				ret = 0;
			} else {
				cmdData.setExecflag(DevCmdConfData.EXEC_FLAG_NO);
				cmdData.setExectime(null);
				ret = 1;
			}
			String rspInfo = "["+retcode+"]"+retJsonStr;
			logger.info("execCmd rsp="+rspInfo);
			this.devCmdConfMapper.updateDevCmdConfData(cmdData);
			logger.debug("updateDevCmdConfData ok: "+cmdData);
		} catch (Exception e) {
			logger.error("execCmd failed: "+cmdData, e);
			ret = 2;
		}
		return ret;
	}
	
	private int parseAckJson(String jsonStr) throws Exception {
		JSONObject json = new JSONObject(jsonStr);
		return Integer.parseInt(JsonUtils.getJsonAttrVal(json, "code", "0"));
	}
	
	private boolean isExecSucc(int retcode){
		return (retcode == CODE_OK || 
				retcode == CODE_OK_REJOIN || 
				retcode == CODE_OK_TOBUF  || 
				retcode == CODE_OK_TOIWG);
	}

}
